﻿using System.Runtime.InteropServices;
using System.Text;
using System;

namespace CDIO
{
    namespace ICCard
    {
        public class Card
        {

            //常量定义
            private const byte BLOCK0_EN = 0x01;//操作第0块
            private const byte BLOCK1_EN = 0x02;//操作第1块
            private const byte BLOCK2_EN = 0x04;//操作第2块
            private const byte NEEDSERIAL = 0x08;//仅对指定序列号的卡操作
            private const byte EXTERNKEY = 0x10;
            private const byte NEEDHALT = 0x20;//读卡或写卡后顺便休眠该卡，休眠后，卡必须拿离开感应区，再放回感应区，才能进行第二次操作。
            private const byte UseAreaNo = 7;//CDIO小组约定，将消费信息写在第07区第02块
            //------------------------------------------------------------------------------------------------------------------------------------------------------
            //外部函数声明：让设备发出声响
            [DllImport("CDIO_Mifare.dll", EntryPoint = "pcdbeep")]
            private static extern byte pcdbeep(ulong xms);//xms单位为毫秒

            //------------------------------------------------------------------------------------------------------------------------------------------------------
            //读卡
            [DllImport("CDIO_Mifare.dll", EntryPoint = "piccreadex")]
            private static extern byte piccreadex(byte ctrlword, byte[] serial, byte area, byte keyA1B0, byte[] picckey, byte[] piccdata0_2);
            //------------------------------------------------------------------------------------------------------------------------------------------------------
            //写卡
            [DllImport("CDIO_Mifare.dll", EntryPoint = "piccwriteex")]
            private static extern byte piccwriteex(byte ctrlword, byte[] serial, byte area, byte keyA1B0, byte[] picckey, byte[] piccdata0_2);
            
            public static ulong ReadBeep = 20;
            public static ulong WriteBeep = 60;
            public static void Beep(ulong during = 20)
            {
                pcdbeep(during);
            }

            public static uint DetectedCard
            {
                get
                {
                    return ReadICSerial();
                }
            }

            private static uint ReadICSerial()
            {
                byte AuthMode= 1;//密码类型，用A密码或B密码
                byte CtrlWord = EXTERNKEY;//控制字
                byte[] ICKey = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
                byte[] ICSerial = new byte[4];//卡序列号
                byte[] dataBuffer = new byte[48]; //卡数据缓冲,不读取数据
                //不读取内容，只读取卡序列号
                byte state = piccreadex(CtrlWord, ICSerial, 0, AuthMode, ICKey, dataBuffer);
                if (state == 0)
                {
                    return System.BitConverter.ToUInt32(ICSerial,0);
                }
                return 0;
            }
             
            private static bool WriteCard(uint CardSerial,ushort CardNo, ulong StuNo, string StuName)
            {
                byte status;//存放返回值
                byte AreaNo = UseAreaNo;//区号
                byte AuthMode = 1;//密码类型，用A密码或B密码
                byte CtrlWord = BLOCK0_EN | BLOCK2_EN | NEEDSERIAL | EXTERNKEY;//控制字,只写2块,32字节
                byte[] ICKey = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };//密码
                byte[] ICSerialNo = BitConverter.GetBytes(CardSerial);//卡序列号
                byte[] dataBuffer = new byte[48]; //卡数据缓冲
                //前8个字节写学号，占用0块0x00-0x08
                BitConverter.GetBytes(StuNo).CopyTo(dataBuffer, 0x00);
                //依时利标准IC卡格式，占用02块,0x20-0x2f
                {
                    BitConverter.GetBytes(CardNo).CopyTo(dataBuffer, 0x20);
                    dataBuffer[0x22] = 0x60;
                    //0x23-0x24为充值次数置0,0x25-0x26为余额,0x28-0x2f存储姓名
                    string ts = String.Format("{0,4}", StuName);
                    Encoding.Convert(
                        //Encoding.Unicode, 
                        Encoding.UTF8,
                        Encoding.GetEncoding("GB2312"), 
                        Encoding.UTF8.GetBytes(ts)
                        ).CopyTo(dataBuffer,0x28);
                    for (int i = 0; i < 8;i++ )
                    {
                        dataBuffer[i + 0x28] += 0x60;
                    }

                }
                status = piccwriteex(CtrlWord, ICSerialNo, AreaNo, AuthMode, ICKey, dataBuffer);
                if (status == 0)
                {
                    Beep(WriteBeep);
                    return true;
                }
                return false;
            }

            //发卡函数，接收学号和姓名作为参数
            public static bool IssueCard(ulong StuNo,ushort CardNo, string StuName,byte CardType)
            {
                uint CardSerial = ReadICSerial();
                if (CardSerial == 0)
                {
                    return false;
                }
                DB.DAO dao = new DB.DAO();
                object result = null;
                result = dao.Excute("insert into cards values(" +
                    CardNo +
                    "," +
                    StuNo +
                    ",0,'" +
                    CardType +
                    "',0)", true);
                if (result == null)
                {
                    return false;
                }
                return WriteCard(CardSerial, CardNo, StuNo, StuName);
            }

            private static bool ChargCard(ushort CardNo,ushort Money,out ushort Balance)
            {
                Balance = 0;
                ushort ReadCardNo = 0;
                byte status;//存放返回值
                byte AreaNo = UseAreaNo;//区号
                byte AuthMode = 1;//密码类型，用A密码或B密码
                byte CtrlWord = BLOCK2_EN | EXTERNKEY;//控制字,只读2块,32字节
                byte[] ICKey = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };//密码
                byte[] ICSerialNo = new byte[1];//不指定卡序列号
                byte[] dataBuffer = new byte[48]; //卡数据缓冲
                status = piccreadex(CtrlWord, ICSerialNo, AreaNo, AuthMode, ICKey, dataBuffer);
                if (status == 0)
                {
                    ReadCardNo = BitConverter.ToUInt16(dataBuffer, 0x20);
                    if (ReadCardNo!=CardNo)
                    {
                        return false;
                    }
                    Balance = BitConverter.ToUInt16(dataBuffer, 0x25);
                    Balance += Money;
                    BitConverter.GetBytes(Balance).CopyTo(dataBuffer, 0x25);
                    status = piccwriteex(CtrlWord, ICSerialNo, AreaNo, AuthMode, ICKey, dataBuffer);
                    if (status == 0)
                    {
                        Beep(WriteBeep);
                        return true;
                    }
                }
                return false;
            }


            public static void Charge(ushort CardNo, ushort Money)
            {
                //卡相关操作
                //Money以角为单位
                ushort balance;
                if (ChargCard(CardNo, Money,out balance))
                {
                    //数据库相关操作
                    CDIO.DB.DAO dao = new CDIO.DB.DAO();
                    if (0 < (int)dao.Excute("insert into tradelog values("+
                        CardNo+",'"+
                        System.DateTime.Now.ToString("yyyy-M-dd hh:mm:ss",System.Globalization.DateTimeFormatInfo.InvariantInfo)+"',"+
                        Money+","+
                        balance
                        +");", true))
                    {
                        return;
                    }
                    System.Windows.Forms.MessageBox.Show("写入数据库失败");
                    return;
                }
                System.Windows.Forms.MessageBox.Show("写卡失败");
            }

            public static void ReadCard(out ushort CardNo,out long StuNo,out string StuName,out ushort Blance)
            {
                StuNo = 0;
                StuName = null;
                Blance = 0;
                CardNo = 0;
                byte status;//存放返回值
                byte AreaNo = UseAreaNo;//区号
                byte AuthMode = 1;//密码类型，用A密码或B密码
                byte CtrlWord = BLOCK0_EN | BLOCK2_EN | EXTERNKEY;//控制字,只读2块,32字节
                byte[] ICKey = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };//密码
                byte[] ICSerialNo = new byte[1];//不指定卡序列号
                byte[] dataBuffer = new byte[48]; //卡数据缓冲

                status = piccreadex(CtrlWord, ICSerialNo, AreaNo, AuthMode, ICKey, dataBuffer);
                if(status == 0)
                {
                    StuNo = BitConverter.ToInt64(dataBuffer, 0);
                    CardNo = BitConverter.ToUInt16(dataBuffer,0x20);
                    Blance = BitConverter.ToUInt16(dataBuffer, 0x25);
                    for (int i = 0; i < 8; i++)
                    {
                        dataBuffer[i + 0x28] -= 0x60;
                    }

                    StuName = Encoding.GetEncoding("GB2312").GetString(dataBuffer, 0x28, 8);
                    Beep();
                }
            }


        }
    }
}